package com.laamella.syntherface.synths.alesis.micron.model.sysex;

import javax.sound.midi.InvalidMidiDataException;

import com.laamella.syntherface.synths.alesis.AlesisUtilities;
import com.laamella.syntherface.synths.alesis.micron.model.Patch;
import com.laamella.syntherface.synths.midi.BinaryData;
import com.laamella.syntherface.synths.midi.BinaryField;
import com.laamella.syntherface.synths.midi.Bits;
import com.laamella.syntherface.synths.models.ParameterModel;
import com.laamella.syntherface.synths.models.ParameterType;

/**
 * The format of the patch sysex message, and files. No data is held in this
 * object itself, it is only used for encoding and decoding.
 * <p>
 * OMFG! The sysex is really a MESS!!!
 * 
 * @author danny
 * 
 */
public class PatchSysEx extends BinaryData {

	private final ParameterModel checksumSize;

	/**
	 * 
	 * @param patch
	 *                The source and destination of the data in the sysex
	 *                message.
	 */
	@SuppressWarnings("serial") public PatchSysEx(Patch patch) {
		super(432);
		// Message header

		// TODO use checksum size for something
		checksumSize = new ParameterModel("checksum size", new ParameterType.IntegerRange(0, 100000)) {
			@Override protected void updateSynthesizer(int value) throws InvalidMidiDataException {
			}
		};
		binaryParameters.add(new AlesisUtilities.UnsignedInt32Field(51, checksumSize));

		binaryParameters.add(new BinaryField.StaticBytesField(0, new byte[] { 0, 0x0E, 0x22 }));
		binaryParameters.add(new BinaryField.StaticTextField(7, "Q01SYNTH"));

		// Voice
		binaryParameters.add(new BinaryField.TextField(63, patch.name, 15));
		binaryParameters.add(new BinaryField.BitField(78, patch.voice.polyphony, 0, 1));
		binaryParameters.add(new BinaryField.BitField(78, patch.voice.portamentoType, 6, 2));
		// TODO not correct
		// binaryParameters.add(new BinaryView.BitField(78,
		// patch.voice.unison, 1, 3));
		binaryParameters.add(new BinaryField.BitField(79, patch.voice.unisonDetune, 0, 7));
		// TODO combine
		// binaryParameters.add(new BinaryView.BitField(79,
		// patch.voice.portamento..., 7, 1));
		binaryParameters.add(new AlesisUtilities.SignedInt8Field(80, patch.voice.portamentoTime));
		binaryParameters.add(new AlesisUtilities.SignedInt8Field(81, patch.voice.pitchwheelType));
		// TODO combine
		// binaryParameters.add(new AlesisUtilities.SignedInt8(82,
		// patch.voice.portamento...));
		binaryParameters.add(new AlesisUtilities.SignedInt8Field(83, patch.voice.analogDrift));
		// binaryParameters.add(new AlesisUtilities.SignedInt8(86,
		// patch.category));

		// Oscillators
		binaryParameters.add(new AlesisUtilities.SignedInt16Field(87, patch.oscillator1.pitch));
		binaryParameters.add(new AlesisUtilities.SignedInt16Field(89, patch.oscillator2.pitch));
		binaryParameters.add(new AlesisUtilities.SignedInt16Field(91, patch.oscillator3.pitch));
		binaryParameters.add(new AlesisUtilities.SignedInt8Field(93, patch.oscillator1.waveShape));
		binaryParameters.add(new AlesisUtilities.SignedInt8Field(94, patch.oscillator2.waveShape));
		binaryParameters.add(new AlesisUtilities.SignedInt8Field(95, patch.oscillator3.waveShape));

		binaryParameters.add(new BinaryField.BitField(96, patch.oscillator1.octave, 0, 3) {
			@Override protected int modifyAfterGetValue(int value) {
				return value + 3;
			}

			@Override protected int modifyBeforeSetValue(int newValue) {
				return newValue - 3;
			}
		});
		binaryParameters.add(new BinaryField.BitField(96, patch.oscillator1.transpose, 4, 4) {
			@Override protected int modifyAfterGetValue(int value) {
				return value + 7;
			}

			@Override protected int modifyBeforeSetValue(int newValue) {
				return newValue - 7;
			}
		});
		binaryParameters.add(new BinaryField.BitField(97, patch.oscillator1.waveform, 0, 2));
		binaryParameters.add(new BinaryField.BitField(97, patch.oscillator1.pitchwheelRange, 2, 4));
		// binaryParameters.add(new BinaryView.BitField(97,
		// patch.voice.oscillatorSync..., 6,1));
		// binaryParameters.add(new BinaryView.BitField(97,
		// patch.voice.oscillatorSync..., 7,1));
		binaryParameters.add(new BinaryField.BitField(98, patch.voice.oscillatorSyncHardDerived, 0, 1));
		// binaryParameters.add(new BinaryView.BitField(98,
		// patch.voice.fmType, 2, 2));
		binaryParameters.add(new BinaryField.BitField(98, patch.oscillator2.waveform, 4, 2));
		binaryParameters.add(new BinaryField.BitField(98, patch.oscillator3.waveform, 6, 2));
		// binaryParameters.add(new BinaryView.BitField(100,
		// patch.voice.fmType, 7, 1));

		binaryParameters.add(new BinaryField.BitField(101, patch.oscillator2.octave, 0, 3) {
			@Override protected int modifyAfterGetValue(int value) {
				return value + 3;
			}

			@Override protected int modifyBeforeSetValue(int newValue) {
				return newValue - 3;
			}
		});
		binaryParameters.add(new BinaryField.BitField(101, patch.oscillator2.transpose, 4, 4) {
			@Override protected int modifyAfterGetValue(int value) {
				return value + 7;
			}

			@Override protected int modifyBeforeSetValue(int newValue) {
				return newValue - 7;
			}
		});
		binaryParameters.add(new BinaryField.BitField(102, patch.oscillator3.octave, 0, 3) {
			@Override protected int modifyAfterGetValue(int value) {
				return value + 3;
			}

			@Override protected int modifyBeforeSetValue(int newValue) {
				return newValue - 3;
			}
		});
		binaryParameters.add(new BinaryField.BitField(102, patch.oscillator3.transpose, 4, 4) {
			@Override protected int modifyAfterGetValue(int value) {
				return value + 7;
			}

			@Override protected int modifyBeforeSetValue(int newValue) {
				return newValue - 7;
			}
		});
		binaryParameters.add(new BinaryField.BitField(103, patch.oscillator1.pitchwheelRange, 0, 4));
		binaryParameters.add(new BinaryField.BitField(103, patch.oscillator1.pitchwheelRange, 4, 4));
		binaryParameters.add(new AlesisUtilities.SignedInt16Field(104, patch.voice.fmAmount));
		// Prefilter mix
		
		// TODO lots more here
		
		// Finally, the checksum.
		binaryParameters.add(new AlesisUtilities.ChecksumField(63, 372));
	}

	@Override protected byte[] preDecodeParameters(byte[] data) throws Exception {
		return AlesisUtilities.convertFromAlesisSysExToByteArray(data);
	}

	@Override protected byte[] postEncodeParameters(byte[] data) throws Exception {
		return AlesisUtilities.convertFromSysExToAlesisByteArray(data);
	}
}
